﻿#include "Codec_NBIoT.h"
#include "qdebug.h"

#include "LogSaver.h"
Codec_NBIoT::Codec_NBIoT()
{
  logSaver = LogSaver::getLogSaver();
  decodeFunctionMap.insert(NSONMI,&Codec_NBIoT::decode_NSONMI);
  decodeFunctionMap.insert(CEREG_feedBack,&Codec_NBIoT::decode_CEREG);
}

Codec_NBIoT::~Codec_NBIoT()
{
}

void Codec_NBIoT::slot_decode(QByteArray newData)
{
  dataBuffer.append(newData);
  //  logSaver->saveLog("new msg:"+newData);
  decode();
}

bool Codec_NBIoT::isRSP()
{
  return RSP;
}

bool Codec_NBIoT::isBaseStationConnected()
{
  return baseStationConnection;
}

void Codec_NBIoT::AT_MIPLCREATE()
{
  //AT+MIPLCREATE
  sendData(new NBIOTInstruction(new QByteArray(MIPLCREATE + CR)));
  //  sendData((MIPLCREATE+CR).toLocal8Bit());
}
void Codec_NBIoT::AT_MIPLDELETE(QString ref)
{
  //AT+MIPLDELETE=<ref>
  sendData(new NBIOTInstruction(new QByteArray((MIPLDELETE + EQUAL + ref + CR).toLocal8Bit())));
  //  sendData((MIPLDELETE+EQUAL+ref+CR).toLocal8Bit());
}
void Codec_NBIoT::AT_MIPLADDOBJ(QString ref, QString objId, QString insCount, QString insBitmap, QString attrCount, QString actCount)
{
  //AT+MIPLADDOBJ=<ref>,<objId>,<insCount>,"<insBitmap>",<attrCount>,<actCount>
  sendData(new NBIOTInstruction(new QByteArray((MIPLADDOBJ + EQUAL + ref + COMMA + objId + COMMA + insCount + COMMA + "\"" + insBitmap + "\"" + COMMA + attrCount + COMMA + actCount + CR).toLocal8Bit())));
  //  sendData((MIPLADDOBJ+EQUAL+ref+COMMA+objId+COMMA+insCount+COMMA+"\""+insBitmap+"\""+COMMA+attrCount+COMMA+actCount+CR).toLocal8Bit());
}
void Codec_NBIoT::AT_MIPLDELOBJ(QString ref, QString objId)
{
  //AT+MIPLDELOBJ=0,3200
  sendData(new NBIOTInstruction(new QByteArray((MIPLDELOBJ + EQUAL + ref + COMMA + objId + CR).toLocal8Bit())));
  //  sendData((MIPLDELOBJ+EQUAL+ref+COMMA+objId+CR).toLocal8Bit());
}
void Codec_NBIoT::AT_MIPLOPEN(QString ref, QString lifeTime, QString timeOut)
{
  //AT+MIPLOPEN=0,600,60
  sendData(new NBIOTInstruction(new QByteArray((MIPLOPEN + EQUAL + ref + COMMA + lifeTime + COMMA + timeOut + CR).toLocal8Bit())));
  //  sendData((MIPLOPEN+EQUAL+ref+COMMA+lifeTime+COMMA+timeOut+CR).toLocal8Bit());
}
void Codec_NBIoT::AT_MIPLCLOSE(QString ref)
{
  //AT+MIPLCLOSE=0
  instructionQueue.clear();
  RSP = true;
  sendData(new NBIOTInstruction(new QByteArray((MIPLCLOSE + EQUAL + ref + CR).toLocal8Bit())));
  //  sendData((MIPLCLOSE+EQUAL+ref+CR).toLocal8Bit());
}
void Codec_NBIoT::AT_MIPLDISCOVERRSP(QString ref, QString msgid, QString result, QString valueString)
{
  //AT+MIPLDISCOVERRSP=0,msgid,1,4,"5750"
  sendData(new NBIOTInstruction(new QByteArray((MIPLDISCOVERRSP + EQUAL + ref + COMMA + msgid + COMMA + result + COMMA + QString::number(valueString.length()) + COMMA + "\"" + valueString + "\"" + CR).toLocal8Bit())));
}
void Codec_NBIoT::AT_MIPLWRITERSP(QString ref, QString msgid, QString result)
{
  //AT+MIPLWRITERSP=0,msgid,2
  sendData(new NBIOTInstruction(new QByteArray((MIPLWRITERSP + EQUAL + ref + COMMA + msgid + COMMA + result + CR).toLocal8Bit())));
}
void Codec_NBIoT::AT_MIPLNOTIFY(QString ref, QString msgid, QString objId, QString insId, QString resId, QString valueType, QString value)
{
  //AT+MIPLNOTIFY=0,12345,3200,0,5750,1,5,"12345",0,0
  sendData(new NBIOTInstruction(new QByteArray((MIPLNOTIFY + EQUAL + ref + COMMA + msgid + COMMA + objId + COMMA + insId + COMMA + resId + COMMA + valueType + COMMA + QString::number(value.length()) + COMMA + "\"" + value + "\"" + COMMA + "0" + COMMA + "0" + CR).toLocal8Bit())));
}

void Codec_NBIoT::AT_CEREG()
{
  logSaver->saveLog("check NB connection state");
  sendData(new NBIOTInstruction(new QByteArray(CEREG + CR)));
}

void Codec_NBIoT::AT_NSOCR(QString port)
{
  //AT+NSOCR=DGRAM,17,9000,1
  logSaver->saveLog("create socket");
  sendData(new NBIOTInstruction(new QByteArray((NSOCR + EQUAL + "DGRAM" + COMMA + "17" + COMMA + port + COMMA + "1" + CR).toLocal8Bit()),
                                &Codec_NBIoT::decode_noHead_CREATESOCKET));
}

void Codec_NBIoT::AT_NSOST(QString socketID, QString targetHostAddress, QString targetPort, QByteArray data)
{
  //AT+NSOST=<socket>,<remote_addr>,<remote_port>,<length>,<data>[,<sequence>]
  sendData(new NBIOTInstruction(new QByteArray((NSOST + EQUAL + socketID + COMMA + targetHostAddress + COMMA + targetPort + COMMA + QString::number(data.size()) + COMMA + data.toHex() + CR).toLocal8Bit()),
                                &Codec_NBIoT::decode_noHead_SENDUDPDATA,
                                &Codec_NBIoT::handleError_AT_NSOST));
}

void Codec_NBIoT::AT_NSORF(QString socketID, QString len)
{
  //AT+NSORF=<socket>,<req_length>
  //设置读取数据的指令
  readDataInstruction = new NBIOTInstruction(new QByteArray((NSORF + EQUAL + socketID + COMMA + len + CR).toLocal8Bit()),
                                             &Codec_NBIoT::decode_noHead_READSOCKET);
  sendData1();
  //sendDataImmediately(new NBIOTInstruction(new QByteArray((NSORF+EQUAL+socketID+COMMA+len+CR).toLocal8Bit()),
  //                                           &Codec_NBIoT::decode_noHead_READSOCKET));
}
void Codec_NBIoT::AT_NSOCL(QString socketID)
{
  //AT+NSOCL=<socket>
  sendData(new NBIOTInstruction(new QByteArray((NSOCL + EQUAL + socketID + CR).toLocal8Bit())));
}

void Codec_NBIoT::decode() //解析数据缓冲区
{
  while (dataBuffer.size() > currentIndex)
    {
      //数据缓冲区大小dataBuffer.size()大于当前解析位置currentIndex时
      //意味着有新的数据添加到缓冲区，需要继续解析
      const char currentChar = dataBuffer.at(currentIndex); //当前正在解析的字符
      if (CR == currentChar || LF == currentChar)
        {
          if(0 == currentIndex)
            {
              //如果首个字符为'\r'或'\n'则丢弃
              dataBufferDropData(1);
            }
          else
            {
              //开始解析
              //如果currentDecode不为零的情况下，currentChar为'\r'，
              //则说明缓冲区头部是一条大小为currentDecode的完整指令
              //需要立刻开始解析，数据大小等于currentIndex;
              char * const dataPtr = dataBuffer.data();
              if (isEqual(dataPtr,OK.data(),OK.size())) //解析出OK
                {
                  decode_OK();
                }
              else if (isEqual(dataPtr,ERROR.data(),ERROR.size())) //解析出ERROR
                {
                  decode_ERROR();
                }
              else
                {
                  int colonIndex=findColon(dataPtr,currentIndex);
                  if(-1==colonIndex)
                    {
                      decode_noHead(currentIndex);
                    }
                  else
                    {
                      (this->*decodeFunctionMap.value(dataBufferGetData(colonIndex)))(currentIndex);
                    }
                }
              dataBufferDropData(currentIndex);
              currentIndex=0;
            }
        }
      else
        {
          //其他字符则保留
          ++currentIndex;
        }
    }
}

void Codec_NBIoT::sendData(NBIOTInstruction *nBIOTInstruction)
{
  instructionQueue.append(nBIOTInstruction); //将新的指令加入发送队列末尾
  sendData1();                               //尝试发送数据
}

void Codec_NBIoT::sendDataImmediately(NBIOTInstruction *nBIOTInstruction)
{
  instructionQueue.prepend(nBIOTInstruction);
  sendData1();
}

void Codec_NBIoT::sendData1()
{
  if (!RSP)
    {
      //如果上一条指令尚未得到反馈， 则不能发送数据
      return;
    }
  if (Q_NULLPTR != readDataInstruction)
    {
      //若有新的消息需要读，则应当先读
      RSP = false;
      handleNohead = readDataInstruction->handle;     //记录无头部的处理函数
      handleError = readDataInstruction->handleError; //记录处理ERROR的函数
      lastNBIOTInstruction = Q_NULLPTR;
      serialPortManager->sendData(*readDataInstruction->getInstruction()); //发送读取数据指令
      delete readDataInstruction;      //删除对应的内存
      readDataInstruction = Q_NULLPTR; //指针置零
    }
  else if (!instructionQueue.isEmpty()) //如果发送队列不为空
    {
      NBIOTInstruction *temp = instructionQueue.takeFirst();
      RSP = false;
      handleNohead = temp->handle;     //记录无头部的处理函数
      handleError = temp->handleError; //记录处理ERROR的函数
      if(lastNBIOTInstruction != temp)
        {
          if(Q_NULLPTR != lastNBIOTInstruction)
            {
              //如果上一条指令不等于本次指令且不为空，则清理上条指令的内存
              delete lastNBIOTInstruction;
            }
            //将上一条指令指针指向本指令
            lastNBIOTInstruction = temp;
        }//与本指令一样则说明是重发指令，什么也不做
      serialPortManager->sendData(*temp->getInstruction()); //发送指令队列中第一个指令
      logSaver->saveLog("send data to NB:" + *temp->getInstruction());
    }
}

void Codec_NBIoT::decode_OK()
{
  logSaver->saveLog("OK");
  RSP = true;
  sendData1();
}

void Codec_NBIoT::decode_ERROR()
{
  logSaver->saveLog("ERROR");
  RSP = true;
  if (Q_NULLPTR != handleError)
    {
      (this->*handleError)();
    }
  sendData1();
}

void Codec_NBIoT::decode_MIPLCONFIG(int lengthOfInstruction)
{
  QStringList argList = getArgList(lengthOfInstruction, MIPLCONFIG.size() + 1);
  emit signal_MIPLCONFIG(argList[0], argList[1], argList.last());
  dataBufferDropData(lengthOfInstruction);
}
void Codec_NBIoT::decode_MIPLDISCOVER(int lengthOfInstruction)
{
  QStringList argList = getArgList(lengthOfInstruction, MIPLDISCOVER.size() + 1);
  emit signal_MIPLDISCOVER(argList[0], argList[1], argList[2]);
}
void Codec_NBIoT::decode_MIPLEVENT(int lengthOfInstruction)
{
  QStringList argList = getArgList(lengthOfInstruction, MIPLEVENT.size() + 1);
  emit signal_MIPLEVENT(argList[0], argList[1]);
}
void Codec_NBIoT::decode_MIPLWRITE(int lengthOfInstruction)
{
  QStringList argList = getArgList(lengthOfInstruction, MIPLWRITE.size() + 1);
  emit signal_MIPLWRITE(argList[0], argList[1], argList[2], argList[3], argList[4], argList[5], argList[6], QByteArray::fromHex(argList[7].toUtf8()), argList[8], argList[9]);
}
void Codec_NBIoT::decode_NSONMI(int lengthOfInstruction)
{
  QStringList argList = getArgList(lengthOfInstruction, NSONMI.size()+1);
  AT_NSORF(argList[0], argList[1]);
}

void Codec_NBIoT::decode_CEREG(int lengthOfInstruction)
{
  QStringList argList = getArgList(lengthOfInstruction, CEREG_feedBack.size() + 1);
  if ('1' == argList[1])
    {
      baseStationConnection = true;
    }
  else
    {
      baseStationConnection = false;
    }
  logSaver->saveLog(QString("current connection state:")+(baseStationConnection?"connected":"disconnected"));
}

void Codec_NBIoT::decode_noHead(int lengthOfInstruction)
{
  if (Q_NULLPTR != handleNohead)
    {
      (this->*handleNohead)(lengthOfInstruction);
    }
}
void Codec_NBIoT::decode_noHead_CREATESOCKET(int lengthOfInstruction)
{
  QStringList argList = getArgList(lengthOfInstruction, 0);
  logSaver->saveLog("creat socket:" + argList[0]);
  emit signal_newSocketId(argList[0]);
}
void Codec_NBIoT::decode_noHead_SENDUDPDATA(int lengthOfInstruction)
{
  QStringList argList = getArgList(lengthOfInstruction, 0);
  logSaver->saveLog("send data:" + argList[0] + COMMA + argList[1]);
}
void Codec_NBIoT::decode_noHead_READSOCKET(int lengthOfInstruction)
{
  QStringList argList = getArgList(lengthOfInstruction, 0);
  logSaver->saveLog("read data:" + argList[0] + COMMA + argList[4].toUtf8());
  emit signal_newSocketMsg(argList[0], argList[1], argList[2].toUShort(), argList[3].toInt(), QByteArray::fromHex(argList[4].toUtf8()));
  if (0 < argList[5].toInt())
    {
      logSaver->saveLog("still have data:" + argList[0] + COMMA + argList[5]);
      AT_NSORF(argList[0], argList[5]);
    }
}

void Codec_NBIoT::handleError_AT_NSOST()
{
  logSaver->saveLog("send again");
  sendDataImmediately(lastNBIOTInstruction);
}

void Codec_NBIoT::dataBufferDropData(int len)
{
  dataBuffer.remove(0, len);
}
QByteArray Codec_NBIoT::dataBufferGetData(int len)
{
  return dataBuffer.mid(0, len);
}

QStringList Codec_NBIoT::getArgList(int len, int begin)
{
  return ((QString)dataBufferGetData(len).mid(begin)).split(COMMA, QString::SkipEmptyParts);
}

bool Codec_NBIoT::isEqual(const char *charPtr1, const char *charPtr2, int len)
{
  --len;
  for(;len>-1;--len)
    {
      if(charPtr1[len]!=charPtr2[len])
        {
          return false;
        }
    }
  return true;
}

int Codec_NBIoT::findColon(const char * charPtr,int len)
{
  --len;
  for(;len>-1;--len)
    {
      if(':'==charPtr[len])
        {
          return len;
        }
    }
  return -1;
}

SerialPortManager *Codec_NBIoT::getSerialPortManager()
{
  return serialPortManager;
}
void Codec_NBIoT::setSerialPortManager(SerialPortManager *serialPortManager)
{
  if (Q_NULLPTR != this->serialPortManager)
    {
      disconnect(this->serialPortManager, &SerialPortManager::signal_dataReceived, this, &Codec_NBIoT::slot_decode);
    }
  this->serialPortManager = serialPortManager;
  printInjection(this, serialPortManager);
  if (Q_NULLPTR != this->serialPortManager)
    {
      connect(this->serialPortManager, &SerialPortManager::signal_dataReceived, this, &Codec_NBIoT::slot_decode, Qt::QueuedConnection);
    }
}

void Codec_NBIoT::objectAutoInjection(QHash<QString, QObject *> *objMap)
{
  setSerialPortManager(getObjFromMap<SerialPortManager>(objMap));
}
